#include "timer.h"

#include "../rpc_root.h"
#include "object_pool.h"
#include "rpc_singleton.h"
#include <chrono>
#include <ctime>
#include <ratio>
#include <unordered_map>

using namespace std::chrono;

std::time_t getMilliSeconds() {
  auto now = system_clock::now();
  auto millionSecondsDuration =
      duration_cast<milliseconds>(now.time_since_epoch());
  return millionSecondsDuration.count();
}

class TimerFactoryImpl : public TimerFactory {
public:
  TimerFactoryImpl() {}
  virtual ~TimerFactoryImpl() {}
  virtual TimerBase *create() override {
    static Timer timer;
    return &timer;
  }
  virtual void destory(TimerBase *timer_base) override { return; }
};

TimerFactoryImpl defaulTimerFactory;
TimerFactory *global_timer_factory = &defaulTimerFactory;

void setTimerFactory(TimerFactory *factory) { global_timer_factory = factory; }

TimerFactory *getTimerFactory() { return global_timer_factory; }

TimerHandle Timer::addTimerOnce(TimerFunc timer_func, std::time_t duration,
                                std::uint64_t user_data) {
  TimerHandle timerHandle;
  timerHandle = rpc::make_shared_ptr<TimerNode_>();
  timerHandle->timer_func = timer_func;
  timerHandle->type = Type_::ONCE;
  timerHandle->duration = duration;
  timerHandle->trigger_time = getMilliSeconds() + duration;
  timerHandle->user_data = user_data;
  queue_.emplace(timerHandle);
  return timerHandle;
}

Timer::Timer() {}

Timer::~Timer() { clear(); }

TimerHandle Timer::addTimer(TimerFunc timer_func, std::time_t duration,
                            std::uint64_t user_data) {
  TimerHandle timerHandle;
  timerHandle = rpc::make_shared_ptr<TimerNode_>();
  timerHandle->timer_func = timer_func;
  timerHandle->type = Type_::LOOP;
  timerHandle->duration = duration;
  timerHandle->trigger_time = getMilliSeconds() + duration;
  timerHandle->user_data = user_data;
  queue_.emplace(timerHandle);
  return timerHandle;
}

void Timer::cancelTimer(TimerHandle handle) {
  handle->deleted = true;
  handle->timer_func = nullptr;
}

TimerHandle Timer::runOnce(std::time_t now) {
  auto handle = runOnce_(now);
  if (handle) {
    if (handle->timer_func) {
      handle->timer_func(handle->user_data);
    }
    return handle;
  } else {
    return nullptr;
  }
}

void Timer::clear() {}

TimerHandle Timer::runOnce_(std::time_t now) {
  auto milli_seconds = now;
  if (!now) {
    milli_seconds = getMilliSeconds();
  }
  while (!queue_.empty()) {
    auto handle = queue_.top();
    if (handle->deleted) {
      queue_.pop();
      continue;
    }
    if (queue_.top()->trigger_time < milli_seconds) {
      queue_.pop();
      switch (handle->type) {
      case Type_::LOOP:
        handle->trigger_time = milli_seconds + handle->duration;
        queue_.push(handle);
        break;
      default:
        break;
      }
      return handle;
    } else {
      break;
    }
  }
  return nullptr;
}
